﻿using System;
using System.IO.Ports;
using System.Text;
using System.Threading;

namespace Library.Gsm {
  public abstract class SerialConnection : IGsmConnection {
    private SerialPort _serialPort;
    private AutoResetEvent _reReceiveSignal;
    protected SerialConnection() {
      _serialPort = new SerialPort();
    }

    public bool IsOpen {
      get { return _serialPort != null && _serialPort.IsOpen; }
    }

    public void Open(IConnectionInfo info) {
      if (IsOpen) throw new InvalidOperationException("SerialConnection is opened");

      _reReceiveSignal = new AutoResetEvent(false);
      _serialPort.BaudRate = (Int32)info.Get("BaudRate");
      _serialPort.DataBits = (Int32)info.Get("DataBits");
      _serialPort.Encoding = (Encoding)info.Get("Encoding");
      _serialPort.Parity = (Parity)info.Get("Parity");
      _serialPort.PortName = (String)info.Get("PortName");
      _serialPort.ReadTimeout = (Int32)info.Get("ReadTimeOut");
      _serialPort.StopBits = (StopBits)info.Get("StopBits");
      _serialPort.WriteTimeout = (Int32)info.Get("WriteTimeOut");
      _serialPort.DataReceived += (SerialPortDataReceived);
      _serialPort.Open();
      _serialPort.DtrEnable = true;
      _serialPort.RtsEnable = true;
    }



    void SerialPortDataReceived(object sender, SerialDataReceivedEventArgs e) {
      if (e.EventType == SerialData.Chars)
        _reReceiveSignal.Set();
    }

    public void Close() {
      if (IsOpen) {
        _serialPort.Close();
        _serialPort.DataReceived -= (SerialPortDataReceived);
        _serialPort = null;
      }
    }


    public abstract IGsmCommand CreateCommand();

    public int ReadTimeOut {
      get { return _serialPort.ReadTimeout; }
      set { _serialPort.ReadTimeout = value; }
    }

    public int WriteTimeOut {
      get { return _serialPort.WriteTimeout; }
      set { _serialPort.WriteTimeout = value; }
    }

    public void Write(string command) {
      _serialPort.Write(command);
    }

    public bool ReadResponseLimitTimeOut(out string response) {
      if (_reReceiveSignal.WaitOne(ReadTimeOut, false)) {
        response = _serialPort.ReadExisting();
        return true;
      }

      response = String.Empty;
      return false;
    }

    public bool ReadResponseLimitTimeOut(out string response, string readToString) {
      if (_reReceiveSignal.WaitOne(ReadTimeOut, false)) {
        response = _serialPort.ReadTo(readToString);
        return true;
      }

      response = String.Empty;
      return false;
    }

    public void BeforeExecuteCommand() {
      _serialPort.DiscardInBuffer();

      _serialPort.DiscardOutBuffer();
      _reReceiveSignal.Reset();
    }
  }
}
